/**
 * @author        Nicolas Bottarini <nicolasbottarini@gmail.com>, 404 <http://www.proyecto404.com>
 * @version       $Rev: 3 $
 * @lastrevision  $Date: 2011-09-12 16:45:37 +0000 (Mon, 12 Sep 2011) $	 
 */
package com.proyecto404.core.mvc {
	import com.proyecto404.core.Check;
	import com.proyecto404.core.Configuration;
	import com.proyecto404.core.EventArgs;
	import com.proyecto404.core.TranslationManager;
	import com.proyecto404.core.collections.Dictionary;
	import com.proyecto404.core.ui.BaseMovieClip;
	import com.proyecto404.core.ui.util.DisplayObjectHelper;
	
	import com.adobe.protocols.dict.Dict;
	
	import flash.display.DisplayObject;
	import flash.display.FrameLabel;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import org.osflash.signals.ISignal;
	import org.osflash.signals.Signal;
	
	// TODO: Organizar mejor los metodos
	public class View extends BaseMovieClip {
		public const introLabel:String = "intro";
		public const mainLabel:String = "main";
		public const outroLabel:String = "outro";
		
		private var _closed:Signal;
		private var _opened:Signal;
		private var _viewState:ViewStates;
		private var _application:Application;
		private var _hasIntro:Boolean;
		private var _hasOutro:Boolean;
		private var _model:*;
		
		public function View() {
			super();
			stop();
			_closed = new Signal(EventArgs);
			_opened = new Signal(EventArgs);
			_viewState = ViewStates.Inactive;
			mouseChildren = false;
			mouseEnabled = false;

			_hasIntro = labels.containsKey(introLabel);
			_hasOutro = labels.containsKey(outroLabel);
			addEventListener(Event.ADDED, function(e:Event):void {
				if (e.currentTarget == DisplayObject(e.target).parent) {
					onChildAdded(e.target as DisplayObject);
				}
			});
			
			addEventListener(Event.ADDED_TO_STAGE, onPrivateAddedToStage);
		}
		
		public function get viewName():String {
			return getClassName();
		}
		
		public function initialize(application:Application, model:* = null):void {
			_application = application;
			_model = model;

			onInit();	
		}
		
		public function get application():Application {
			return _application;
		}

		public function get model():* {
			return _model;
		}

		public function get library():Library {
			return _application.library;
		}

		public function get translations():TranslationManager {
			return _application.translations;
		}

		public function get configuration():Configuration {
			return _application.configuration;
		}
		
		public function get hasIntro():Boolean {
			return _hasIntro;
		}
		
		// TODO: Ver de mejorar esto
		protected function declareIntro():void {
			_hasIntro = true;
		}

		public function get hasOutro():Boolean {
			return _hasOutro;
		}

		// TODO: Ver de mejorar esto
		protected function declareOutro():void {
			_hasOutro = true;
		}
		
		public function get firstMainFrame():int {
			if (labels.containsKey(mainLabel)) {
				return FrameLabel(labels.get(mainLabel)).frame;
			} 
			
			return 1;
		} 
		
		public function get lastMainFrame():int {
			if (labels.containsKey(outroLabel)) {
				return Math.max(FrameLabel(labels.get(outroLabel)).frame - 1, 1);
			} else {
				return totalFrames;
			}
		}
		
		public function get hasMainTimeLine():Boolean {
			return firstMainFrame != lastMainFrame;
		}

		public function open():void {
			Check.require(_viewState == ViewStates.Inactive, "View is already opened");
			
			_viewState = ViewStates.Opening;

			if (hasIntro) {
				intro();
			} else {
				doOpen();
			}
		}
		
		protected function notifyEndIntro():void {
			if (_viewState != ViewStates.Closing) {
				doOpen();
			} else {
				// Pending close
				_opened.dispatch(new EventArgs());
				startClose();
			}
		}

		private function onOpeningEnterFrame(e:Event):void {
			if (currentFrameLabel == mainLabel) {
				removeEventListener(Event.ENTER_FRAME, onOpeningEnterFrame);
				notifyEndIntro();
			}
		}
		
		public function close():void {
			Check.require(_viewState != ViewStates.Inactive, "View is not opened");
			Check.require(_viewState != ViewStates.Closed, "View is already closed");
			
			if (_viewState == ViewStates.Opening) {
				_viewState = ViewStates.Closing;
			} else if (_viewState == ViewStates.Active) {
				startClose();
			}
		}
		
		private function doOpen():void {
			stop();
			_viewState = ViewStates.Active;
			mouseChildren = true;
			mouseEnabled = true;
			onOpen();
			
			_opened.dispatch(new EventArgs());
			
			if (hasMainTimeLine) {
				//main();
				registerFrameFunction(firstMainFrame, main);
				registerFrameFunction(lastMainFrame, mainEnd);
				play();
			} else {
				main();
			}
		}
		
		protected function mainEnd():void {
			stop();
			onMainEnd();
		}
		
		protected function main():void {
			onMain();
		}

		private function onPrivateAddedToStage(e:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			onAddedToStage();
		}
		
		// BEGIN OVERRIDABLE METHODS
		
		protected function onInit():void {}
		
		protected function onOpen():void {}
		
		protected function onMain():void {}
		
		protected function onMainEnd():void {}
		
		protected function onClose():void {}
		
		protected function onChildAdded(child:DisplayObject):void {}
		
		protected function onAddedToStage():void {}
		
		protected function intro():void {
			gotoAndPlay(introLabel);
			addEventListener(Event.ENTER_FRAME, onOpeningEnterFrame);
		}
		
		protected function outro():void {
			gotoAndPlay(outroLabel);
			addEventListener(Event.ENTER_FRAME, onClosingEnterFrame);
		}

		// END OVERRIDABLE METHODS 
		
		private function startClose():void {
			_viewState = ViewStates.Closing;
			mouseChildren = false;
			mouseEnabled = false;
			if (hasOutro) {
				outro();
			} else {
				doClose();
			}
		}
		
		protected function notifyEndOutro():void {
			doClose();
		}
		
		private function doClose():void {
			removeEventListener(Event.ENTER_FRAME, onClosingEnterFrame);
			_viewState = ViewStates.Closed;
			stop();
			onClose();
			_closed.dispatch(new EventArgs());
		}
		
		private function onClosingEnterFrame(e:Event):void {
			if (currentFrame == totalFrames) {
				notifyEndOutro();
			}
		}
		
		public function get closed():ISignal {
			return _closed;
		}

		public function get opened():ISignal {
			return _opened;
		}
		
		public function isOpened():Boolean {
			return _viewState == ViewStates.Active;
		}
		
		public function isClosed():Boolean {
			return _viewState == ViewStates.Closed;
		}

		public function removeAllChildren():void {
			DisplayObjectHelper.removeAllChildren(this);
		}
		
		protected function setCloseButton(obj:DisplayObject):void {
			obj.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
				close();
			});
		}

		// Notification Helpers
		

		protected function sendNotification(name:String, parameters:Object = null):void {
			Check.argument.isNotEmpty(name, "name");
			
			application.dispatchNotification(new Notification(this, name, new Dictionary(parameters)));
		}
		
		protected function clickNotification(obj:DisplayObject, notificationName:String, parameters:Object = null):void {
			Check.argument.isNotNull(obj, "obj");
			Check.argument.isNotEmpty(notificationName, "notificationName");
			
			obj.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
				sendNotification(notificationName, parameters);
			});
		}
		
		protected function OutroRewind():void {
			rewind(notifyEndOutro);
		}
	}
}